#include <string.h>
#include <stdlib.h>
#include <wiringPi.h>
#include <wiringPiSPI.h>
#include <softPwm.h>
#include "lcd_2inch4.h"

void Lcd_Gpio_Init(void)
{
    printf("Init gpio for wiringPi\r\n");
    if (wiringPiSetupGpio() == -1) exit(1);
    if (wiringPiSPISetup(0, 8000000) == -1) exit(1);
    pinMode(PIN_RST, OUTPUT);
    pinMode(PIN_DC, OUTPUT);
    pinMode(PIN_BL, OUTPUT);
    softPwmCreate(PIN_BL, 0, 100);
    softPwmWrite(PIN_BL, 90);
}

static void Lcd_Reset(void)
{
    digitalWrite(PIN_RST, HIGH);
    delay(1);
    digitalWrite(PIN_RST, LOW);
    delay(80);
    digitalWrite(PIN_RST, HIGH);
    delay(120);
}

static void Spi_write_cmd(uint8_t data)
{
    digitalWrite(PIN_DC, LOW);
    wiringPiSPIDataRW(0, &data, 1);
}

static void Spi_write_data(uint8_t data)
{
    digitalWrite(PIN_DC, HIGH);
    wiringPiSPIDataRW(0, &data, 1);
}

void Spi_write_word(uint16_t data)
{
    uint8_t buffer[2];
    buffer[0] = (uint8_t)(data >> 8);
    buffer[1] = (uint8_t)data;
    digitalWrite(PIN_DC, HIGH);
    wiringPiSPIDataRW(0, buffer, 2);
}

void Lcd_Init(void)
{
    Lcd_Gpio_Init();
    Lcd_Reset();

    Spi_write_cmd(0x01); delay(120);
    Spi_write_cmd(0x11); delay(120);
    Spi_write_cmd(0xCF); Spi_write_data(0x00); Spi_write_data(0xC1); Spi_write_data(0x30);
    Spi_write_cmd(0xED); Spi_write_data(0x64); Spi_write_data(0x03); Spi_write_data(0x12); Spi_write_data(0x81);
    Spi_write_cmd(0xE8); Spi_write_data(0x85); Spi_write_data(0x00); Spi_write_data(0x79);
    Spi_write_cmd(0xCB); Spi_write_data(0x39); Spi_write_data(0x2C); Spi_write_data(0x00); Spi_write_data(0x34); Spi_write_data(0x02);
    Spi_write_cmd(0xF7); Spi_write_data(0x20);
    Spi_write_cmd(0xEA); Spi_write_data(0x00); Spi_write_data(0x00);
    Spi_write_cmd(0xC0); Spi_write_data(0x1D);
    Spi_write_cmd(0xC1); Spi_write_data(0x12);
    Spi_write_cmd(0xC5); Spi_write_data(0x33); Spi_write_data(0x3F);
    Spi_write_cmd(0xC7); Spi_write_data(0x92);
    Spi_write_cmd(0x3A); Spi_write_data(0x55);
    Spi_write_cmd(0x36); Spi_write_data(0x28); /* landscape + BGR */
    Spi_write_cmd(0xB1); Spi_write_data(0x00); Spi_write_data(0x12);
    Spi_write_cmd(0xB6); Spi_write_data(0x0A); Spi_write_data(0xA2);
    Spi_write_cmd(0x44); Spi_write_data(0x02);
    Spi_write_cmd(0xF2); Spi_write_data(0x00);
    Spi_write_cmd(0x26); Spi_write_data(0x01);

    Spi_write_cmd(0xE0);
    Spi_write_data(0x0F); Spi_write_data(0x22); Spi_write_data(0x1C); Spi_write_data(0x1B);
    Spi_write_data(0x08); Spi_write_data(0x0F); Spi_write_data(0x48); Spi_write_data(0xB8);
    Spi_write_data(0x34); Spi_write_data(0x05); Spi_write_data(0x0C); Spi_write_data(0x09);
    Spi_write_data(0x0F); Spi_write_data(0x07); Spi_write_data(0x00);

    Spi_write_cmd(0xE1);
    Spi_write_data(0x00); Spi_write_data(0x23); Spi_write_data(0x24); Spi_write_data(0x07);
    Spi_write_data(0x10); Spi_write_data(0x07); Spi_write_data(0x38); Spi_write_data(0x47);
    Spi_write_data(0x4B); Spi_write_data(0x0A); Spi_write_data(0x13); Spi_write_data(0x06);
    Spi_write_data(0x30); Spi_write_data(0x38); Spi_write_data(0x0F);

    Spi_write_cmd(0x11); delay(120);
    Spi_write_cmd(0x29); delay(20);
    Lcd_Clear(0x0000);
}

void Set_Window(uint16_t start_x, uint16_t start_y, uint16_t end_x, uint16_t end_y)
{
    Spi_write_cmd(0x2A);
    Spi_write_data((uint8_t)(start_x >> 8));
    Spi_write_data((uint8_t)start_x);
    Spi_write_data((uint8_t)((end_x - 1U) >> 8));
    Spi_write_data((uint8_t)(end_x - 1U));

    Spi_write_cmd(0x2B);
    Spi_write_data((uint8_t)(start_y >> 8));
    Spi_write_data((uint8_t)start_y);
    Spi_write_data((uint8_t)((end_y - 1U) >> 8));
    Spi_write_data((uint8_t)(end_y - 1U));

    Spi_write_cmd(0x2C);
}

void Set_Cursor(uint16_t x, uint16_t y)
{
    Set_Window(x, y, (uint16_t)(x + 1U), (uint16_t)(y + 1U));
}

void Lcd_Clear(uint16_t color)
{
    uint16_t row[LCD_W];
    uint16_t i;
    uint16_t data = (uint16_t)((color >> 8) | (color << 8));

    for (i = 0; i < LCD_W; i++)
        row[i] = data;

    Set_Window(0, 0, LCD_W, LCD_H);
    digitalWrite(PIN_DC, HIGH);
    for (i = 0; i < LCD_H; i++)
        wiringPiSPIDataRW(0, (unsigned char *)row, LCD_W * 2U);
}

void Display(uint16_t *image)
{
    uint16_t i;
    Set_Window(0, 0, LCD_W, LCD_H);
    digitalWrite(PIN_DC, HIGH);
    for (i = 0; i < LCD_H; i++)
        wiringPiSPIDataRW(0, (unsigned char *)image + LCD_W * 2U * i, LCD_W * 2U);
}

void Draw_Point(uint16_t x, uint16_t y, uint16_t color)
{
    Set_Cursor(x, y);
    Spi_write_word(color);
}
